home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / vim_src.zip / CSEARCH.C < prev    next >
C/C++ Source or Header  |  1993-01-12  |  9KB  |  427 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMitation
  4.  *
  5.  * Code Contributions By:    Bram Moolenaar            mool@oce.nl
  6.  *                            Tim Thompson            twitch!tjt
  7.  *                            Tony Andrews            onecom!wldrdg!tony
  8.  *                            G. R. (Fred) Walter     watmath!watcgl!grwalter
  9.  */
  10.  
  11. /*
  12.  *
  13.  * csearch.c: command line searching commands
  14.  */
  15.  
  16. #include "vim.h"
  17. #include "globals.h"
  18. #include "proto.h"
  19. #include "param.h"
  20.  
  21. /* we use modified Henry Spencer's regular expression routines */
  22. #include "regexp.h"
  23.  
  24. int global_busy = 0;            /* set to 1 if global busy, 2 if global has
  25.                                     been called during a global command */
  26. int global_wait;                /* set to 1 if wait_return has to be called
  27.                                     after global command */
  28. extern regexp *myregcomp __ARGS((char *));
  29.  
  30. /* dosub(lp, up, cmd)
  31.  *
  32.  * Perform a substitution from line 'lp' to line 'up' using the
  33.  * command pointed to by 'cmd' which should be of the form:
  34.  *
  35.  * /pattern/substitution/gc
  36.  *
  37.  * The trailing 'g' is optional and, if present, indicates that multiple
  38.  * substitutions should be performed on each line, if applicable.
  39.  * The trailing 'c' is optional and, if present, indicates that a confirmation
  40.  * will be asked for each replacement.
  41.  * The usual escapes are supported as described in the regexp docs.
  42.  */
  43.  
  44. extern char *reg_prev_sub;        /* this is in regexp.c */
  45.  
  46.     void
  47. dosub(lp, up, cmd, nextcommand)
  48.     linenr_t    lp;
  49.     linenr_t    up;
  50.     char        *cmd;
  51.     u_char        **nextcommand;
  52. {
  53.     linenr_t        lnum;
  54.     long            i;
  55.     char           *ptr;
  56.     regexp           *prog;
  57.     long            nsubs = 0;
  58.     linenr_t        nlines = 0;
  59.     int                do_all;         /* do multiple substitutions per line */
  60.     int                do_ask;         /* ask for confirmation */
  61.     char           *pat, *sub;
  62.     static char    *old_sub = NULL;
  63.     int             delimiter;
  64.     int             sublen;
  65.  
  66.     if (strchr("0123456789gc|\"#", *cmd) == NULL)       /* new pattern and substitution */
  67.     {
  68.         delimiter = *cmd++;            /* remember delimiter character */
  69.         pat = cmd;                    /* remember the start of the regexp */
  70.  
  71.         /*
  72.          * do the next loop twice:
  73.          *  i == 0: find the end of the regexp
  74.          *  i == 1: find the end of the substitution
  75.          */
  76.         for (i = 0; ; ++i)
  77.         {
  78.             while (cmd[0])
  79.             {
  80.                 if (cmd[0] == delimiter)            /* end delimiter found */
  81.                 {
  82.                     *cmd++ = NUL;                    /* replace it by a NUL */
  83.                     break;
  84.                 }
  85.                 if (cmd[0] == '\\' && cmd[1] != 0)    /* skip escaped characters */
  86.                     ++cmd;
  87.                 ++cmd;
  88.             }
  89.             if (i == 1)
  90.                 break;
  91.             sub = cmd;                /* remember the start of the substitution */
  92.         }
  93.         free(old_sub);
  94.         old_sub = strsave(sub);
  95.     }
  96.     else                                /* use previous pattern and substitution */
  97.     {
  98.         if (old_sub == NULL)    /* there is no previous command */
  99.         {
  100.             beep();
  101.             return;
  102.         }
  103.         pat = NULL;             /* myregcomp() will use previous pattern */
  104.         sub = old_sub;
  105.     }
  106.  
  107.     /*
  108.      * find trailing options
  109.      */
  110.     do_all = FALSE;
  111.     do_ask = FALSE;
  112.     while (*cmd)
  113.     {
  114.         if (*cmd == 'g')
  115.             do_all = TRUE;
  116.         else if (*cmd == 'c')
  117.             do_ask = TRUE;
  118.         else
  119.             break;
  120.         ++cmd;
  121.     }
  122.  
  123.     /*
  124.      * check for a trailing count
  125.      */
  126.     skipspace(&cmd);
  127.     if (isdigit(*cmd))
  128.     {
  129.         i = getdigits(&cmd);
  130.         if (i <= 0)
  131.         {
  132.             emsg(e_zerocount);
  133.             return;
  134.         }
  135.         lp = up;
  136.         up += i - 1;
  137.     }
  138.  
  139.     /*
  140.      * check for trailing '|', '"' or '#'
  141.      */
  142.     skipspace(&cmd);
  143.     if (*cmd)
  144.     {
  145.         if (strchr("|\"#", *cmd) != NULL)
  146.         {
  147.             *nextcommand = (u_char *)cmd;
  148.         }
  149.         else
  150.         {
  151.             emsg(e_trailing);
  152.             return;
  153.         }
  154.     }
  155.  
  156.     if ((prog = myregcomp(pat)) == NULL)
  157.     {
  158.         emsg(e_invcmd);
  159.         return;
  160.     }
  161.  
  162.     for (lnum = lp; lnum <= up && !got_int; ++lnum)
  163.     {
  164.         ptr = nr2ptr(lnum);
  165.         if (regexec(prog, ptr, (int)TRUE))  /* a match on this line */
  166.         {
  167.             char       *ns, *sns = NULL, *p, *prevp, *oldp = NULL;
  168.             int            did_sub = FALSE;
  169.  
  170.             if (nsubs == 0)
  171.                     setpcmark();
  172.             /*
  173.              * Save the line that was last changed for the final cursor
  174.              * position (just like the real vi).
  175.              */
  176.             Curpos.lnum = lnum;
  177.  
  178.             prevp = p = ptr;
  179.             do
  180.             {
  181.                 Curpos.col = prog->startp[0] - ptr;
  182.                 /*
  183.                  * First match empty string does not count, except for first match.
  184.                  * This reproduces the strange vi behaviour.
  185.                  * This also catches endless loops.
  186.                  */
  187.                 if (did_sub && p == oldp && p == prog->endp[0])
  188.                 {
  189.                     ++p;
  190.                     goto skip2;
  191.                 }
  192.                 if (do_ask)
  193.                 {
  194.                         updateScreen(CURSUPD);
  195.                         smsg("replace by %s (y/n/q)? ", sub);
  196.                         setcursor();
  197.                         if ((i = vgetc()) == 'q')
  198.                         {
  199.                             got_int = TRUE;
  200.                             break;
  201.                         }
  202.                         else if (i != 'y')
  203.                             goto skip;
  204.                 }
  205.  
  206.                         /* get length of substitution part */
  207.                 sublen = regsub(prog, sub, ptr, 0, (int)p_magic);
  208.                 if (did_sub == FALSE)
  209.                 {
  210.                     /*
  211.                      * Get some space for a temporary buffer to do the substitution
  212.                      * into.
  213.                      */
  214.                     if ((sns = alloc((unsigned)(strlen(ptr) + sublen + 5))) == NULL)
  215.                         goto outofmem;
  216.                     *sns = NUL;
  217.                     did_sub = TRUE;
  218.                 }
  219.                 else
  220.                 {
  221.                     /*
  222.                      * extend the temporary buffer to do the substitution into.
  223.                      */
  224.                     if ((ns = alloc((unsigned)(strlen(sns) + strlen(prevp) + sublen + 1))) == NULL)
  225.                         goto outofmem;
  226.                     strcpy(ns, sns);
  227.                     free(sns);
  228.                     sns = ns;
  229.                 }
  230.  
  231.                 for (ns = sns; *ns; ns++)
  232.                     ;
  233.                 /*
  234.                  * copy up to the part that matched
  235.                  */
  236.                 while (prevp < prog->startp[0])
  237.                     *ns++ = *prevp++;
  238.  
  239.                 regsub(prog, sub, ns, 1, (int)p_magic);
  240.                 nsubs++;
  241.                 /*
  242.                  * Regsub may have replaced a ~ by the old sub.
  243.                  * We have to use the result, otherwise the ~ is replaced
  244.                  * over and over again.
  245.                  */
  246.                 sub = reg_prev_sub;
  247.  
  248.                 prevp = prog->endp[0];    /* remember last copied character */
  249.                 /*
  250.                  * continue searching after the match
  251.                  * prevent endless loop with patterns that match empty strings,
  252.                  * e.g. :s/$/pat/g or :s/[a-z]* /(&)/g
  253.                  */
  254. skip:
  255.                 p = prog->endp[0];
  256.                 oldp = p;
  257.                 if (*p == NUL)      /* end of line: quit here */
  258.                     break;
  259.  
  260. skip2:
  261.                     /* breakcheck is slow, don't call it too often */
  262.                 if ((nsubs & 15) == 0)
  263.                     breakcheck();
  264.  
  265.             } while (!got_int && do_all && regexec(prog, p, (int)FALSE));
  266.  
  267.             if (did_sub)
  268.             {
  269.                     /*
  270.                      * copy the rest of the line, that didn't match
  271.                      */
  272.                     strcat(sns, prevp);
  273.  
  274.                     if ((ptr = save_line(sns)) != NULL)
  275.                             u_savesub(lnum, replaceline(lnum, ptr));
  276.  
  277.                     free(sns);          /* free the temp buffer */
  278.                     ++nlines;
  279.             }
  280.         }
  281.             /* breakcheck is slow, don't call it too often */
  282.         if ((lnum & 15) == 0)
  283.             breakcheck();
  284.     }
  285.  
  286. outofmem:
  287.     if (nsubs)
  288.     {
  289.         CHANGED;
  290.         updateScreen(CURSUPD); /* need this to update LineSizes */
  291.         beginline(TRUE);
  292.         if (nsubs >= p_report)
  293.             smsg("%s%ld substitution%s on %ld line%s",
  294.                                 got_int ? "(Interrupted) " : "",
  295.                                 nsubs, plural(nsubs),
  296.                                 (long)nlines, plural((long)nlines));
  297.         else if (got_int)
  298.                 msg(e_interr);
  299.         else if (do_ask)
  300.                 msg("");
  301.     }
  302.     else if (got_int)
  303.         msg(e_interr);
  304.     else
  305.         msg("No match");
  306.  
  307.     free((char *) prog);
  308. }
  309.  
  310. /*
  311.  * doglob(cmd)
  312.  *
  313.  * Execute a global command of the form:
  314.  *
  315.  * g/pattern/X : execute X on all lines where pattern matches
  316.  * v/pattern/X : execute X on all lines where pattern does not match
  317.  *
  318.  * where 'X' is an EX command
  319.  *
  320.  * The command character (as well as the trailing slash) is optional, and
  321.  * is assumed to be 'p' if missing.
  322.  *
  323.  * This is implemented in two passes: first we scan the file for the pattern and
  324.  * set a mark for each line that (not) matches. secondly we execute the command
  325.  * for each line that has a mark. This is required because after deleting
  326.  * lines we do not know where to search for the next match.
  327.  */
  328.  
  329.     void
  330. doglob(type, lp, up, cmd)
  331.     int         type;
  332.     linenr_t    lp, up;
  333.     char        *cmd;
  334. {
  335.     linenr_t        lnum;        /* line number according to old situation */
  336.     linenr_t        old_lcount; /* line_count before the command */
  337.     int             ndone;
  338.  
  339.     char            delim;        /* delimiter, normally '/' */
  340.     char           *pat;
  341.     regexp           *prog;
  342.     int                match;
  343.  
  344.     if (global_busy)
  345.     {
  346.         emsg("Cannot do :global recursive");
  347.         ++global_busy;
  348.         return;
  349.     }
  350.  
  351.     delim = *cmd++;             /* skip the delimiter */
  352.     pat = cmd;
  353.  
  354.     while (cmd[0])
  355.     {
  356.         if (cmd[0] == delim)                /* end delimiter found */
  357.         {
  358.             *cmd++ = NUL;                    /* replace it by a NUL */
  359.             break;
  360.         }
  361.         if (cmd[0] == '\\' && cmd[1] != 0)    /* skip escaped characters */
  362.             ++cmd;
  363.         ++cmd;
  364.     }
  365.  
  366.     reg_ic = p_ic;           /* set "ignore case" flag appropriately */
  367.  
  368.     if ((prog = myregcomp(pat)) == NULL)
  369.     {
  370.         emsg(e_invcmd);
  371.         return;
  372.     }
  373.     msg("");
  374.  
  375. /*
  376.  * pass 1: set marks for each (not) matching line
  377.  */
  378.     ndone = 0;
  379.     for (lnum = lp; lnum <= up && !got_int; ++lnum)
  380.     {
  381.         match = regexec(prog, nr2ptr(lnum), (int)TRUE);     /* a match on this line? */
  382.         if ((type == 'g' && match) || (type == 'v' && !match))
  383.         {
  384.             setmarked(lnum);
  385.             ndone++;
  386.         }
  387.             /* breakcheck is slow, don't call it too often */
  388.         if ((lnum & 15) == 0)
  389.             breakcheck();
  390.     }
  391.  
  392. /*
  393.  * pass 2: execute the command for each line that has been marked
  394.  */
  395.     if (got_int)
  396.         msg("Interrupted");
  397.     else if (ndone == 0)
  398.         msg("No match");
  399.     else
  400.     {
  401.         global_busy = 1;
  402.         global_wait = 0;
  403.         RedrawingDisabled = TRUE;
  404.         old_lcount = line_count;
  405.         while (!got_int && (lnum = firstmarked()) != 0 && global_busy == 1)
  406.         {
  407.             Curpos.lnum = lnum;
  408.             Curpos.col = 0;
  409.             if (*cmd == NUL)
  410.                 docmdline((u_char *)"p");
  411.             else
  412.                 docmdline((u_char *)cmd);
  413.             breakcheck();
  414.         }
  415.  
  416.         RedrawingDisabled = FALSE;
  417.         if (global_wait)                /* wait for return */
  418.             wait_return(FALSE);
  419.         updateScreen(CLEAR);
  420.         msgmore(line_count - old_lcount);
  421.     }
  422.  
  423.     clearmarked();      /* clear rest of the marks */
  424.     global_busy = 0;
  425.     free((char *) prog);
  426. }
  427.